package chaussitag.storagespy;

import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;

import android.os.FileObserver;
import android.util.Log;

public class RecursiveFileObserver {

	private static final String TAG = "RecursiveFileObserver";
	
	public static interface FileEventListener {
		public void onEvent(int event, String path, Date time);
	}

	/** Only modification events */
	public static int CHANGES_ONLY =
			FileObserver.CREATE | FileObserver.DELETE
			| FileObserver.CLOSE_WRITE | FileObserver.MOVE_SELF
			| FileObserver.MOVED_FROM | FileObserver.MOVED_TO;

	private FileEventListener mFileEventListener;

	private Map<String, SingleFileObserver> mObservers;
	private int mMask;

	public RecursiveFileObserver(int eventMask) {
		mMask = eventMask;
	}

	public void setFileEventListener(FileEventListener fileEventListener) {
		mFileEventListener = fileEventListener;
	}

	public boolean startWatching(String[] dirs) {
		if (mObservers != null)
			return false;

		Stack<String> stack = new Stack<String>();
		for (String dirPath : dirs) {
			stack.push(dirPath);
		}

		mObservers = new HashMap<String, SingleFileObserver>();
		while (!stack.isEmpty()) {
			String parent = stack.pop();
			mObservers.put(parent, new SingleFileObserver(parent, mMask));
			File path = new File(parent);
			File[] files = path.listFiles();
			if (null == files)
				continue;
			for (File f : files) {
				if (f.isDirectory() && !f.getName().equals(".") && !f.getName().equals("..")) {
					stack.push(f.getAbsolutePath());
				}
			}
		}

		for (Entry<String, SingleFileObserver> sfo : mObservers.entrySet()) {
			sfo.getValue().startWatching();
		}

		return true;
	}

	public void stopWatching() {
		if (mObservers == null)
			return;
		
		for (Entry<String, SingleFileObserver> sfo : mObservers.entrySet()) {
			sfo.getValue().stopWatching();
		}
		mObservers.clear();
		mObservers = null;
	}

	class SingleFileObserver extends FileObserver {
		String mPath;
		
		public SingleFileObserver(String path) {
			this(path, ALL_EVENTS);
			mPath = path;
		}

		public SingleFileObserver(String path, int mask) {
			super(path, mask);
			mPath = path;
		}

		@Override
		public void onEvent(int event, String path) {
			StringBuilder builder = new StringBuilder();
			builder.append(mPath);
			if (path != null) {
				builder.append(File.separator).append(path);
			}

			String fullPath = builder.toString();
			File file;
			int action = event & FileObserver.ALL_EVENTS;
			boolean notifyListener = false; // 只通知文件夹的创建、删除、移动，其它都不通知
			switch (action) {
			case FileObserver.CREATE:
				file = new File(fullPath);
				if (file.isDirectory()) {
					Log.d(TAG, "add observer for " + fullPath);
					SingleFileObserver observer = new SingleFileObserver(fullPath, mMask);
					mObservers.put(fullPath, observer);
					observer.startWatching();
					notifyListener = true;
				}
				break;
			case FileObserver.DELETE_SELF:
				Log.d(TAG, fullPath + " was removed");
				mObservers.remove(fullPath);
				notifyListener = true;
				break;
			case FileObserver.MOVED_FROM:
				// 如果被mObservers包含，那么移动的是文件夹
				if (mObservers.containsKey(fullPath)) {
					SingleFileObserver observer = mObservers.remove(fullPath);
					if (observer != null) {
						Log.d(TAG, "stop watching " + fullPath);
						observer.stopWatching();
					}
					notifyListener = true;
				}
				break;
			case FileObserver.MOVED_TO:
				file = new File(fullPath);
				if (file.isDirectory()) {
					Log.d(TAG, "start watching " + fullPath);
					SingleFileObserver observer = new SingleFileObserver(fullPath, mMask);
					mObservers.put(fullPath, observer);
					observer.startWatching();
					notifyListener = true;
				}
				break;
		    default:
			    break;
			}

			if (notifyListener && mFileEventListener != null && fullPath != null) {
				mFileEventListener.onEvent(action, fullPath, new Date());
			}
		}
	}
}